# -*- coding: utf-8 -*-
import re, collections, time, json
import solar, www
from os import urandom
from hashlib import sha1
from string import Template
from base64 import standard_b64encode as b64e

def ws(func):
    def _ws(args, kwargs, cid):
        func.func_globals['browsers'] = global_browsers
        func.func_globals['client'] = js_callback = \
            global_browsers.get(cid)
        func.func_globals['callback'] = eval( \
            'js_callback.' + kwargs.get('callback', '_pass'))
        res = func(args, kwargs)
        return res
    return _ws

class browsers(object):
    def __init__(self):
        self.queue = {}

    def __call__(self, httpfile):
        cid = httpfile.environ['QUERY_STRING'] \
            or sha1(urandom(16)).hexdigest()
        if cid not in self.queue:
            client = self.queue[cid] = browser(flashsocket(httpfile), cid)
            # client.setcid(cid) #返回 cid 给客户端
            while 1:
                try:
                    args, kwargs = client.read(180.)
                    if args:
                        url = args.pop(0)
                        fn = url.split('/')[-1]
                        path = '/'.join(url.split('/')[:-1])
                        kwargs['cid'] = cid
                        if hasattr(www.sites.get(path), fn):
                            eval(''.join(['www.sites["', path, '"].', fn])) \
                                (args, kwargs, cid=cid)
                    else:
                        break
                except solar.core.timeout:
                    break
            try:
                self.queue.pop(client.cid)
            except KeyError:
                pass
    
    def __len__(self):
        return len(self.queue)
    
    def __getitem__(self, cid):
        return self.queue[cid]
        
    def get(self, cid):
        return self.queue.get(cid)

class browser(object):
    def __init__(self, ws, cid):
        self.ws = ws
        self.cid = cid

    def __call__(self, *args, **kwargs):
        self.ws.write(
            rpcstr(func=kwargs['func'],
                   args=', '.join(json.dumps(arg) for arg in args)),
            kwargs.get('timeout', -1))

    def __getattr__(self, func):
        return remotecall(self.ws, func)

    def _pass(self, *args, **kwargs):
        pass

    def read(self, timeout):
        return self.ws.read(timeout)

class remotecall(object):
    def __init__(self, socket, func):
        self.__socket = socket
        self.__func = func

    def __getattr__(self, func):
        return remotecall(self.__socket,
            '%s.%s'%(self.__func, func))

    def __getitem__(self, func):
        if isinstance(func, unicode):
            return remotecall(self.__socket,
                '%s[%s]'%(self.__func, repr(func)[1:]))
        return remotecall(self.__socket,
            '%s[%s]'%(self.__func, repr(func)))

    def __call__(self, *args, **kwargs):
        self.__socket.write(
            rpcstr(func=self.__func,
                   args=', '.join(json.dumps(arg) for arg in args)), 
            kwargs.get('timeout', -1))
            
rpcstr = Template('${func}(${args});').substitute

class flashsocket(object):
    def __init__(self, httpfile):
        self.socket = httpfile.socket
        
    def __call__(self):
        pass

    def read(self, size=1024, timeout=-1):
        args, kwargs, end = None, None, False
        while 1:
            s = self.socket.readline(size, timeout).rstrip('\n')
            if s:
                if end:
                    kwargs = self.pack_dict(int(s), size, timeout)
                    break
                else:
                    args = self.pack_list(int(s), size, timeout)
                    end = True
            else:
                break
        return args, kwargs
        
    def _read(self, size, timeout):
        vtype, vlen, v = None, None, None
        while 1:
            s = self.socket.readline(size, timeout).rstrip('\n')
            if None == vtype:
                vtype = s
            elif None == vlen:
                try:
                    vlen = int(s)
                    if '\x03' == vtype or '\x04' == vtype:
                        break
                except:
                    break
            else:
                if '\x01' == vtype:
                    if m_float(s):
                        v = float(m_float(s).group())
                    elif m_int(s):
                        v = int(m_int(s).group())
                    else:
                        break
                else:
                    v = str(s)
                break
        return vtype, vlen, v

    def pack_list(self, count, size, timeout):
        lst = []
        while count:
            vtype, vlen, v = self._read(size, timeout)
            if '\x01' == vtype:
                lst.append(v)
            elif '\x02' == vtype:
                lst.append(v)
            elif '\x03' == vtype:
                lst.append(self.pack_list(vlen, size, timeout))
            elif '\x04' == vtype:
                lst.append(self.pack_dict(vlen, size, timeout))
            count -= 1
        return lst
        
    def pack_dict(self, count, size, timeout):
        dct, k, v = {}, None, None
        while count:
            if None==k:
               vtype, vlen, k = self._read(size, timeout) #XXX key一定是字符串
            else:
                vtype, vlen, v = self._read(size, timeout)
                if '\x01' == vtype:
                    dct[k] = v
                elif '\x02' == vtype:
                    dct[k] = v
                elif '\x03' == vtype:
                    dct[k] = self.pack_list(vlen, size, timeout)
                elif '\x04' == vtype:
                    dct[k] = self.pack_dict(vlen, size, timeout)
                k = None
                count -= 1
        return dct

    def write(self, message, timeout=-1):
        self.socket.write(message, timeout)

    def close(self):
        self.socket.close()

import re
m_float = re.compile('\d*\.{1}\d*').match
m_int = re.compile('\d+').match

global_browsers = browsers()